﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace AutoPunchApp
{
    public partial class Form1 : Form
    {
        System.Threading.Timer onDutyTimer = null;
        System.Threading.Timer offDutyTimer = null;
        List<CheckBox> days = new List<CheckBox>();
        List<CheckBox> daysOfWeek = new List<CheckBox>();
        Dictionary<string, List<int>> dateDict = new Dictionary<string, List<int>>();
        bool enable = false;

        public Form1()
        {
            InitializeComponent();
            Init();
        }

        private void Init()
        {
            #region Set days checkbox.
            days.Add(checkBox9); days.Add(checkBox10); days.Add(checkBox11); days.Add(checkBox12);
            days.Add(checkBox13); days.Add(checkBox14); days.Add(checkBox15); days.Add(checkBox16);
            days.Add(checkBox17); days.Add(checkBox18); days.Add(checkBox19); days.Add(checkBox20);
            days.Add(checkBox21); days.Add(checkBox22); days.Add(checkBox23); days.Add(checkBox24);
            days.Add(checkBox25); days.Add(checkBox26); days.Add(checkBox27); days.Add(checkBox28);
            days.Add(checkBox29); days.Add(checkBox30); days.Add(checkBox31); days.Add(checkBox32);
            days.Add(checkBox33); days.Add(checkBox34); days.Add(checkBox35); days.Add(checkBox36);
            days.Add(checkBox37); days.Add(checkBox38); days.Add(checkBox39);

            daysOfWeek.Add(checkBox1); daysOfWeek.Add(checkBox2); daysOfWeek.Add(checkBox3);
            daysOfWeek.Add(checkBox4); daysOfWeek.Add(checkBox5); daysOfWeek.Add(checkBox6);
            daysOfWeek.Add(checkBox7);

            #endregion
            this.dateTimePickerMonth.Value = DateTime.Now;
            SetMonthDays();
        }

        void cb_CheckedChanged(object sender, EventArgs e)
        {
            if (!enable) { return; }
            CheckBox checkBox = (CheckBox)sender;
            string key = this.dateTimePickerMonth.Value.ToString("yyyy-MM");
            int index = days.IndexOf(checkBox) + 1;

            if (dateDict.ContainsKey(key))
            {
                if (checkBox.Checked)
                {
                    if (!dateDict[key].Contains(index)) { dateDict[key].Add(index); };
                }
                else
                {
                    if (dateDict[key].Contains(index)) { dateDict[key].Remove(index); };
                }
            }
            else
            {
                dateDict.Add(key, new List<int>());
                if (checkBox.Checked)
                {
                    dateDict[key].Add(index);
                }
            }
        }

        private void rdbOnceDay_CheckedChanged(object sender, EventArgs e)
        {
            this.panel1.Visible = this.rdbOnceDay.Checked;
            this.panel2.Visible = this.rdbWeek.Checked;
            this.panel3.Visible = this.rdbMonth.Checked;

        }

        private void dateTimePicker4_ValueChanged(object sender, EventArgs e)
        {
            SetMonthDays();
        }

        private void SetMonthDays()
        {
            enable = false;
            int dayCount = DateTime.DaysInMonth(this.dateTimePickerMonth.Value.Year, this.dateTimePickerMonth.Value.Month);
            days.ForEach(x => { x.Visible = false; x.Checked = false; });

            for (int i = 0; i < dayCount; i++)
            {
                days[i].Visible = true;
            }

            string key = this.dateTimePickerMonth.Value.ToString("yyyy-MM");
            if (dateDict.ContainsKey(key))
            {
                for (int i = 0; i < dayCount; i++)
                {
                    days[i].Checked = dateDict[key].Contains(i);
                }
            }
            enable = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            switch (Helper.Config.PunchDateType)
            {
                case DateType.Once:
                    this.rdbOnceDay.Checked = true;
                    this.dateTimePickerOnce.Value = Helper.Config.OnceDate.IsValid() ? Helper.Config.OnceDate : DateTime.Now;

                    break;
                case DateType.Week:
                    this.rdbWeek.Checked = true;
                    break;
                case DateType.Days:
                    this.rdbMonth.Checked = true;
                    break;
            }
            switch (Helper.Config.AfterOffDutyActon)
            {
                case Action.Nothing:
                    this.rdbNothing.Checked = true;
                    break;
                case Action.Shutdown:
                    this.rdbPoweroff.Checked = true;
                    break;
                case Action.Suspend:
                    this.rdbSleep.Checked = true;
                    break;
            }
            TimeSpan timeSpan = TimeSpan.FromTicks(Helper.Config.OnDutyTime);
            this.dateTimePickerBegin.Value = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
                timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
            timeSpan = TimeSpan.FromTicks(Helper.Config.OffDutyTime);
            this.dateTimePickerEnd.Value = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
                timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);

            if (Helper.Config.PunchMonthDays == null) { return; }
            string[] selectedDate = Helper.Config.PunchMonthDays.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string d in selectedDate)
            {
                DateTime date;
                if (!DateTime.TryParse(d, out date)) { continue; }
                string month = date.ToString("yyyy-MM");
                if (dateDict.ContainsKey(month))
                {
                    if (!dateDict[month].Contains(date.Day))
                    {
                        dateDict[month].Add(date.Day);
                    }
                }
                else
                {
                    dateDict.Add(month, new List<int>());
                    dateDict[month].Add(date.Day);
                }
            }

            string key = this.dateTimePickerMonth.Value.ToString("yyyy-MM");
            if (dateDict.ContainsKey(key))
            {
                dateDict[key].ForEach(x =>
                {
                    days[x].Checked = true;
                });
            }

            selectedDate = Helper.Config.PunchWeekDays.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string d in selectedDate)
            {
                int day = 0;
                if (!int.TryParse(d, out day)) { continue; }
                if (day < daysOfWeek.Count)
                {
                    daysOfWeek[day].Checked = true;
                }
            }

            days.ForEach(x => { x.CheckedChanged += cb_CheckedChanged; });
            SetTimer();
            ShowMessage();
        }

        //Save config.
        private void button1_Click(object sender, EventArgs e)
        {
            if (this.rdbOnceDay.Checked)
            {
                Helper.Config.PunchDateType = DateType.Once;
                Helper.Config.OnceDate = this.dateTimePickerOnce.Value;
            }
            else if (this.rdbMonth.Checked)
            {
                Helper.Config.PunchDateType = DateType.Days;
            }
            else
            {
                Helper.Config.PunchDateType = DateType.Week;
            }
            if (this.rdbNothing.Checked)
            {
                Helper.Config.AfterOffDutyActon = Action.Nothing;
            }
            else if (this.rdbPoweroff.Checked)
            {
                Helper.Config.AfterOffDutyActon = Action.Shutdown;
            }
            else
            {
                Helper.Config.AfterOffDutyActon = Action.Suspend;
            }

            Helper.Config.OnDutyTime = this.dateTimePickerBegin.Value.TimeOfDay.Ticks;
            Helper.Config.OffDutyTime = this.dateTimePickerEnd.Value.TimeOfDay.Ticks;
            StringBuilder sb = new StringBuilder();
            dateDict.Keys.ToList().ForEach(k =>
            {
                dateDict[k].ForEach(d =>
                {
                    sb.Append(k + "-" + d.ToString().PadLeft(2, '0') + ",");
                });
            });
            if (sb.Length > 0)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            Helper.Config.PunchMonthDays = sb.ToString();

            sb.Clear();
            for (int i = 0; i < daysOfWeek.Count; i++)
            {
                if (daysOfWeek[i].Checked)
                {
                    sb.Append(i).Append(',');
                }
            }
            if (sb.Length > 0)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            Helper.Config.PunchWeekDays = sb.ToString();

            try
            {
                Helper.SaveConfig();
                Program.WriterLog("保存配置信息成功！");
                SetTimer();
            }
            catch (Exception ex)
            {
                Program.WriterLog("保存配置信息失败：出现错误！\n" + ex.Message);
            }
        }

        private void SetTimer()
        {
            if (onDutyTimer != null)
            {
                onDutyTimer.Dispose();
                onDutyTimer = null;
            }

            if (offDutyTimer != null)
            {
                offDutyTimer.Dispose();
                offDutyTimer = null;
            }
            Random random = new Random(DateTime.Now.Millisecond);

            onDutyTimer = new System.Threading.Timer(OnDuty);
            TimeSpan timeSpan = new TimeSpan(Helper.Config.OnDutyTime);
            var dateTime0 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).Add(timeSpan);

            if ((DateTime.Now.TimeOfDay - timeSpan).Ticks > 0)
            {
                dateTime0 = dateTime0.AddDays(1);
            }
            var time = dateTime0 - DateTime.Now;
            time = time.Add(new TimeSpan(0, -random.Next(10), -random.Next(60)));
            if (time.Ticks < 0)
            {
                time = new TimeSpan(0);
            }
            onDutyTimer.Change((int)time.TotalMilliseconds, 24 * 60 * 60 * 1000);
            Program.WriterLog("Onduty计时器已重新设定："+time.ToString());

            offDutyTimer = new System.Threading.Timer(OffDuty);
            timeSpan = new TimeSpan(Helper.Config.OffDutyTime);
            var dateTime1 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).Add(timeSpan);
            if ((DateTime.Now.TimeOfDay - timeSpan).Ticks > 0)
            {
                dateTime1 = dateTime0.AddDays(1);
            }
            time = dateTime1 - DateTime.Now;
            time = time.Add(new TimeSpan(0, random.Next(10), random.Next(60)));
            offDutyTimer.Change((int)time.TotalMilliseconds, 24 * 60 * 60 * 1000);
            Program.WriterLog("Offduty计时器已重新设定：" + time.ToString());
        }

        private void ShowMessage()
        {
            Task task = new Task(new System.Action(() =>
            {
                while (true)
                {
                    TimeSpan timeSpan = new TimeSpan(Helper.Config.OnDutyTime);
                    var dateTime0 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).Add(timeSpan);
                    if ((DateTime.Now.TimeOfDay - timeSpan).Ticks > 0)
                    {
                        dateTime0 = dateTime0.AddDays(1);
                    }
                    var onDutyTimeSpan = dateTime0 - DateTime.Now;

                    timeSpan = new TimeSpan(Helper.Config.OffDutyTime);
                    var dateTime1 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).Add(timeSpan);
                    if ((DateTime.Now.TimeOfDay - timeSpan).Ticks > 0)
                    {
                        dateTime1 = dateTime1.AddDays(1);
                    }
                    var offDutyTimeSpan = dateTime1 - DateTime.Now;

                    this.Invoke(new MethodInvoker(delegate()
                    {
                        this.labMessage.Text = "离上班打卡还有：" + onDutyTimeSpan.ToString(@"hh\:mm\:ss") + "，离下班打卡还有：" + offDutyTimeSpan.ToString(@"hh\:mm\:ss");
                    }));

                    Thread.Sleep(1000);
                }
            }));

            task.Start();
        }

        private void OnDuty(object state)
        {
            PunchResult result;
            try
            {
                switch (Helper.Config.PunchDateType)
                {
                    case DateType.Once:
                        Program.WriterLog("开始打上班卡(一次)...");
                        result = Program.OnDuty();
                        Program.WriterLog("打卡结果：" + result);
                        onDutyTimer.Change(Timeout.Infinite, Timeout.Infinite);
                        break;
                    case DateType.Week:
                        if (daysOfWeek[(int)DateTime.Now.DayOfWeek].Checked)
                        {
                            Program.WriterLog("开始打上班卡(周)...");
                            result = Program.OnDuty();
                            Program.WriterLog("打卡结果：" + result);
                            SetTimer();//重新计算定时器
                        }
                        break;
                    case DateType.Days:
                        if (dateDict[DateTime.Now.ToString("yyyy-MM")].Contains(DateTime.Now.Day))
                        {
                            Program.WriterLog("开始打上班卡(月)...");
                            result = Program.OnDuty();
                            Program.WriterLog("打卡结果：" + result);
                            SetTimer();//重新计算定时器
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                Program.WriterLog("打上班卡计时器方法运行出错：\n" + ex.Message);
            }
        }

        private void OffDuty(object state)
        {
            PunchResult result;
            try
            {
                switch (Helper.Config.PunchDateType)
                {
                    case DateType.Once:
                        Program.WriterLog("开始打下班卡(一次)...");
                        result = Program.OffDuty();
                        Program.WriterLog("打卡结果：" + result);
                        onDutyTimer.Change(Timeout.Infinite, Timeout.Infinite);
                        break;
                    case DateType.Week:
                        if (daysOfWeek[(int)DateTime.Now.DayOfWeek].Checked)
                        {
                            Program.WriterLog("开始打下班卡(周)...");
                            result = Program.OffDuty();
                            Program.WriterLog("打卡结果：" + result);
                        }
                        break;
                    case DateType.Days:
                        if (dateDict[DateTime.Now.ToString("yyyy-MM")].Contains(DateTime.Now.Day))
                        {
                            Program.WriterLog("开始打下班卡(月)...");
                            result = Program.OffDuty();
                            Program.WriterLog("打卡结果：" + result);
                        }
                        break;
                }
                AfterOffDutyAction();
            }
            catch (Exception ex)
            {
                Program.WriterLog("打下班卡计时器方法运行出错：\n" + ex.Message);
            }
        }

        private void AfterOffDutyAction()
        {
            switch (Helper.Config.AfterOffDutyActon)
            {
                case AutoPunchApp.Action.Suspend:
                    Program.SuspendComputer();
                    Program.WriterLog("休眠电脑.");
                    break;
                case AutoPunchApp.Action.Shutdown:
                    Program.ShutdowComputer();
                    Program.WriterLog("关闭电脑.");
                    break;
            }
        }

        private void Form1_Deactivate(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
            }
        }

        private void notifyIcon1_Click(object sender, EventArgs e)
        {
            this.Show();
            this.WindowState = FormWindowState.Normal;
            this.BringToFront();
            this.Activate();
        }
    }
}
